package org.ithasu.filpilote.data;

import java.util.HashMap;

import org.ithasu.filpilote.data.entities.Mode;
import org.ithasu.filpilote.data.entities.Wire;
import org.ithasu.filpilote.data.entities.Zone;
import org.ithasu.tools.TimeTools;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class ControllerModel {

	private final static String JSON_PROPERTY_TIME = "time";
	private final static String JSON_PROPERTY_ZONES = "zones";
	private final static String JSON_PROPERTY_WIRES = "wires";
	private final static String JSON_PROPERTY_INDEX = "index";
	private final static String JSON_PROPERTY_NAME = "name";
	private final static String JSON_PROPERTY_MODE = "mode";
	private final static String JSON_PROPERTY_FORCED = "forced";

	private final ZoneModel zoneModel = new ZoneModel();
	private final HashMap<Zone, Mode> forcedZoneModes = new HashMap<Zone, Mode>();
	private final HashMap<Wire, Mode> forcedWireModes = new HashMap<Wire, Mode>();

	public ControllerModel() {
	}

	// -- Models ----------------------------------------

	public ZoneModel getZoneModel() {
		return zoneModel;
	}

	// -- Time management ----------------------------------------

	public long getLocalTime() {
		return TimeTools.getLocalTime();
	}

	public String getTimeAsTr() {
		return TimeTools.format(getLocalTime());
	}

	// -- Modes ----------------------------------------

	public Mode getZoneMode(Zone zone) {
		Mode forced = forcedZoneModes.get(zone);
		return forced != null ? forced : zoneModel.getZoneMode(zone, getLocalTime());
	}

	public Mode getWireMode(Wire wire) {
		Mode forced = forcedWireModes.get(wire);
		if (forced != null) {
			return forced;
		}
		Zone zone = zoneModel.getWireZone(wire);
		forced = forcedZoneModes.get(zone);
		return forced != null ? forced : zoneModel.getWireMode(wire, getLocalTime());
	}

	// -- Manual modes ----------------------------------------

	public void forceZoneMode(Zone zone, Mode mode) {
		forcedZoneModes.put(zone, mode);
	}

	public void forceWireMode(Wire wire, Mode mode) {
		forcedWireModes.put(wire, mode);
	}

	public boolean isZoneForced(Zone zone) {
		return forcedZoneModes.get(zone) != null;
	}

	public boolean isWireForced(Wire wire) {
		if (forcedWireModes.get(wire) != null) {
			return true;
		}
		Zone zone = zoneModel.getWireZone(wire);
		return forcedZoneModes.get(zone) != null;
	}

	// -- Status ----------------------------------------

	@SuppressWarnings("unchecked")
	public JSONObject getJSONStatus() {
		// -- Local time --
		JSONObject o = new JSONObject();
		o.put(JSON_PROPERTY_TIME, getTimeAsTr());

		// -- Zones --
		JSONArray zones = new JSONArray();
		o.put(JSON_PROPERTY_ZONES, zones);
		for (Zone z : zoneModel.getZones()) {
			if (!zoneModel.hasWires(z)) {
				continue;
			}

			JSONObject zone = new JSONObject();
			zones.add(zone);
			// Zone properties
			zone.put(JSON_PROPERTY_INDEX, zoneModel.getZoneIndex(z));
			zone.put(JSON_PROPERTY_NAME, zoneModel.getZoneName(z));
			zone.put(JSON_PROPERTY_MODE, getZoneMode(z).name());
			zone.put(JSON_PROPERTY_FORCED, isZoneForced(z));

			// Zone's wires
			JSONArray wires = new JSONArray();
			zone.put(JSON_PROPERTY_WIRES, wires);

			for (Wire w : zoneModel.getZoneWires(z)) {
				JSONObject wire = new JSONObject();
				wires.add(wire);
				wire.put(JSON_PROPERTY_INDEX, zoneModel.getWireIndex(w));
				wire.put(JSON_PROPERTY_NAME, zoneModel.getWireName(w));
				wire.put(JSON_PROPERTY_MODE, getWireMode(w).name());
				wire.put(JSON_PROPERTY_FORCED, isWireForced(w));
			}
		}
		return o;
	}

}
